﻿using Dapper;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Ymt.Repository;
using YmtRpc.Domain;
using YmtRpc.Domain.Enum;

namespace YmtRpc.Domain
{

    /// <summary>
    /// 服务依赖采样的仓储类
    /// </summary>
    public class ServiceDependencySamplingRepository : MySQLRepository<ServiceDependencySampling>, IServicedependencysamplingRepository
    {
        public ServiceDependencySamplingRepository()
            : base()
        {

        }

        /// <summary>
        /// 采样数据查询
        /// </summary>
        /// <param name="dependencyRelation"></param>
        /// <returns></returns>
        public List<ServiceDependencySampling> GetServiceDependencySamplingList(ServiceDependencySamplingQueryCondition queryCond, PaginationEntity pageInfo = null)
        {
            List<ServiceDependencySampling> list = new List<ServiceDependencySampling>();

            string where = " WHERE 1 = 1 ";
            string orderby = " EndTime DESC ";

            string sql = "select * from servicedependencysampling ";
            ParametersWrapper par = new ParametersWrapper();

            if (queryCond != null)
            {
                par.Add("AppId", queryCond.AppId).Add("StartDateTime", queryCond.StartDateTime).Add("EndDateTime", queryCond.EndDateTime);

                where += " AND ((BeginTime between @StartDateTime and @EndDateTime) OR (EndTime between @StartDateTime and @EndDateTime)) ";

                if (queryCond.DependencyRelation == DependencyRelationEnum.All)
                {
                    if (!string.IsNullOrEmpty(queryCond.AppId))
                    {
                        where += " AND (ConsumerAppId = @AppId or ProviderAppId = @AppId) ";
                    }
                }
                else
                {
                    if (queryCond.DependencyRelation == DependencyRelationEnum.Dependent)
                    {
                        where += " AND ConsumerAppId = @AppId ";
                    }
                    else
                    {
                        where += " AND ProviderAppId = @AppId ";
                    }
                }

                sql += where;
            }

            if (pageInfo == null)
            {
                sql += " ORDER BY " + orderby;
            }
            else
            {
                string countSql = string.Format("select count(1) from servicedependencysampling {0}", where);

                var totalCount = ExecuteScalar(countSql, par.Build());

                pageInfo.TotalCount = Convert.ToInt32(totalCount);

                if (!string.IsNullOrEmpty(pageInfo.OrderBy))
                {
                    orderby = pageInfo.OrderBy;
                }

                int offset = (pageInfo.PageIndex - 1) * pageInfo.PageSize;
                par.Add("offset", offset).Add("rows", pageInfo.PageSize);

                string pageSQL = " LIMIT @offset, @rows ";

                sql += " ORDER BY " + orderby + pageSQL;
            }

            var result = Query(sql, par.Build());

            if (result != null && result.Any())
            {
                list = result.ToList();
            }

            return list;
        }

        /// <summary>
        /// 根据服务之间的依赖构建依赖树
        /// </summary>
        /// <param name="serviceDependencylist"></param>
        /// <returns></returns>
        public List<ServiceDependencyNode> PopulateDependencyTree(ServiceDependencyQueryCondition cond)
        {
            List<ServiceDependencyNode> realList = new List<ServiceDependencyNode>();            
            List<ServiceDependencyNode> list = new List<ServiceDependencyNode>();
            IEnumerable<ServiceDependencySampling> serviceDependencylist = FindServiceDependencySamplingList();

            Dictionary<string, ServiceDependencyNode> appNodeStore = new Dictionary<string, ServiceDependencyNode>();
            if (serviceDependencylist != null)
            {
                var servicelist = serviceDependencylist.ToList();
                List<string> leafNodes = servicelist.Select(r => r.ProviderAppId).Except(servicelist.Select(r => r.ConsumerAppId)).Distinct().ToList();
                var dependentServiceList = servicelist.Where(r => leafNodes.Contains(r.ProviderAppId));

                while (dependentServiceList.Any())
                {
                    foreach (ServiceDependencySampling dependency in dependentServiceList)
                    {
                        ServiceDependencyNode node = SaveAppNode(appNodeStore, dependency);

                        list.RemoveAll(r => r.name == node.name);
                        list.Add(node);
                    }

                    var appIdList = dependentServiceList.Select(r => r.ConsumerAppId).Distinct().ToList();
                    dependentServiceList = servicelist.Where(r => appIdList.Contains(r.ProviderAppId));
                }

                if (cond != null)
                {
                    switch (cond.DependencyRelation)
                    {
                        case DependencyRelationEnum.All:

                            foreach (ServiceDependencyNode node in list)
                            {
                                if (string.IsNullOrEmpty(cond.AppId) || IsRelyOn(node, cond.AppId, true))
                                {
                                    realList.Add(node);
                                }
                            }

                            break;
                        case DependencyRelationEnum.Dependent:

                            foreach (ServiceDependencyNode node in list)
                            {
                                if (string.IsNullOrEmpty(cond.AppId) || string.Equals(node.name, cond.AppId))
                                {
                                    realList.Add(node);
                                }
                            }

                            break;
                        case DependencyRelationEnum.Relier:

                            foreach (ServiceDependencyNode node in list)
                            {
                                if (string.IsNullOrEmpty(cond.AppId) || IsRelyOn(node, cond.AppId))
                                {
                                    realList.Add(node);
                                }
                            }

                            break;
                    }
                }
            }

            return realList;
        }

        /// <summary>
        /// 根据依赖关系获取服务名列表
        /// </summary>
        /// <param name="dependencyRelation"></param>
        /// <returns></returns>
        public List<string> GetAppIdListByDependencyRelation(DependencyRelationEnum dependencyRelation)
        {
            List<string> list = new List<string>();

            DynamicParameters par = new ParametersWrapper().Build();
            var sql = string.Empty;
            switch (dependencyRelation)
            {
                case DependencyRelationEnum.Dependent:
                    sql = "SELECT DISTINCT ConsumerAppId AS ConsumerAppId FROM servicedependency_info";
                    break;
                case DependencyRelationEnum.Relier:
                    sql = "SELECT DISTINCT ProviderAppId AS ConsumerAppId FROM servicedependency_info";
                    break;
                case DependencyRelationEnum.All:
                    sql = "SELECT DISTINCT ConsumerAppId AS ConsumerAppId FROM servicedependency_info UNION ALL SELECT DISTINCT ProviderAppId AS ConsumerAppId FROM servicedependency_info";
                    break;
            }

            IEnumerable<ServiceDependencySampling> samplings = Query(sql, par, commandType: CommandType.Text);

            foreach (var sampling in samplings)
            {
                list.Add(sampling.ConsumerAppId);
            }

            return list.Distinct().OrderBy(r=>r).ToList();
        }

        private bool IsRelyOn(ServiceDependencyNode node, string appId, bool includeSelf = false)
        {
            List<ServiceDependencyNode> list = new List<ServiceDependencyNode>();

            if (includeSelf && node != null) list.Add(node);
            BuildMultiServiceDependencyNode(list, node);

            if (list.Any(r => string.Equals(r.name, appId)))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public void BuildMultiServiceDependencyNode(List<ServiceDependencyNode> list, ServiceDependencyNode node)
        {
            if (node != null)
            {
                List<ServiceDependencyNode> children = node.children;
                if (children != null)
                {
                    children.ForEach(r => list.Add(new ServiceDependencyNode(){
                        Id = r.Id,
                        pId = r.pId,
                        name = r.name   
                    }));
                    children.ForEach(r =>
                    {
                        BuildMultiServiceDependencyNode(list, r);
                    });
                }
            }
        }
        
        /// <summary>
        /// 将新的服务依赖加入到依赖关系中
        /// </summary>
        /// <param name="appNodeStore">依赖关系 -- 森林</param>
        /// <param name="dependency">服务依赖</param>
        /// <returns></returns>
        private ServiceDependencyNode SaveAppNode(Dictionary<string, ServiceDependencyNode> appNodeStore, ServiceDependencySampling dependency)
        {
            ServiceDependencyNode AppNode = null;
            string appId = dependency.ConsumerAppId;

            if (appNodeStore.ContainsKey(appId))
            {
                AppNode = appNodeStore[appId];
            }
            else
            {
                AppNode = new ServiceDependencyNode()
                {
                    Id = Math.Abs(Guid.NewGuid().GetHashCode()),
                    pId = 0,
                    name = appId
                };

                appNodeStore.Add(appId, AppNode);
            }

            if (AppNode.children == null)
                AppNode.children = new List<ServiceDependencyNode>();

            if (!AppNode.children.Any(r => r.name == dependency.ProviderAppId))
            {
                if (appNodeStore.ContainsKey(dependency.ProviderAppId))
                {
                    ServiceDependencyNode node = appNodeStore[dependency.ProviderAppId];

                    ServiceDependencyNode dependentAppNode = new ServiceDependencyNode()
                    {
                        name = node.name,
                        pId = node.pId,
                        children = node.children
                    };

                    CloneServiceDependencyNode(dependentAppNode, AppNode.Id);

                    AppNode.children.Add(dependentAppNode);
                }
                else
                {
                    AppNode.children.Add(new ServiceDependencyNode()
                    {
                        name = dependency.ProviderAppId,
                        Id = Math.Abs(Guid.NewGuid().GetHashCode()),
                        pId = AppNode.Id
                    });
                }
            }

            return AppNode;
        }

        /// <summary>
        /// 复制子树
        /// </summary>
        /// <param name="dependentAppNode"></param>
        /// <param name="pId"></param>
        private void CloneServiceDependencyNode(ServiceDependencyNode dependentAppNode, int pId)
        {
            int id = Math.Abs(Guid.NewGuid().GetHashCode());
            dependentAppNode.Id = id;
            dependentAppNode.pId = pId;

            if (dependentAppNode.children != null && dependentAppNode.children.Any())
            {
                List<ServiceDependencyNode> list = dependentAppNode.children;
                dependentAppNode.children = new List<ServiceDependencyNode>();

                list.ForEach(r =>
                {
                    dependentAppNode.children.Add(new ServiceDependencyNode()
                    {
                        name = r.name,
                        children = r.children
                    });
                });

                dependentAppNode.children.ForEach(r =>
                {
                    CloneServiceDependencyNode(r, id);
                });
            }
        }

        /// <summary>
        /// 查找符合条件的服务
        /// </summary>
        /// <param name="cond"></param>
        /// <returns></returns>
        public IEnumerable<ServiceDependencySampling> FindServiceDependencySamplingList()
        {
            DynamicParameters par = new ParametersWrapper().Build();         

            var sql = "SELECT DISTINCT ConsumerAppId, ProviderAppId FROM servicedependencysampling";

            return Query(sql, par, commandType: CommandType.Text);
        }
    }
}
